#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include "pwext.hpp"

struct Request
{
	bool isValid()
	{
		return false;
	}
	void handle()
	{ }
};

// [[ex05dd]]
// DEBUG descr Keeping the file descriptor in a dynamically allocated structure causes a race on revocation.
class connection
{
public:
	connection(sockaddr_in &server)
	{ 
		fd_ = ::socket(PF_INET, SOCK_STREAM, 0);
		if (fd_ < 0)
			return;
		if (::connect(fd_, (sockaddr *)&server, 
				sizeof(server)) < 0) {
			::close(fd_);
			fd_ = -1;
		}
	}

	~connection()
	{
		close();
	}

	int read(char *buf, size_t bufsz)
	{
		int fd = fd_;
		if (fd < 0)
			return 0; // EOF if no connection
		return ::read(fd, buf, bufsz);
	}

	void close()
	{
		int fd = fd_;
		fd_ = -1;
		if (fd >= 0)
			::close(fd);
	}

	void revoke()
	{
		if (fd_ < 0)
			return;

		int nullfd;
		nullfd = ::open("/dev/null", O_RDONLY);
		if (nullfd < 0) { 
			// should not happen, but just in case;
			// fail-safe mode, revert to the simple close...
			if (fd_ >= 0) {
				int fd = fd_;
				fd_ = -1;
				usleep(10*1000); // 10ms = 10000 mcs
				::close(fd);
			}
		} else {
			::dup2(nullfd, fd_);
			::close(nullfd);
		}
	}

protected:
	int fd_; // file descriptor to read the requests from
	// ... other information
};

class worker : public pw::pwthread
{
public:
	worker(const sockaddr_in &server) : 
		exit_flag_(false), exit_confirm_flag_(false), 
		server_(server), conn_(0)
	{ }

	void *execute()
	{
		conn_ = new connection(server_);
		while(!get_exit_flag()) {
			struct Request r;

			if (get_exit_flag())
				break;

			int len = conn_->read((char *)&r, sizeof r);
			if (len != sizeof r)
				break;

			if (get_exit_flag())
				break;
			if (!r.isValid())
				continue;

			// ... handle the request...
		}
		delete conn_;
		conn_ = 0;
		{
			pw::lockmutex lm(exit_mutex_);
			exit_confirm_flag_ = true;
		}
		exit_confirm_event_.signal();
	}

	void stop()
	{
		{
			pw::lockmutex lm(exit_mutex_);
			exit_flag_ = true;
		}
		exit_event_.signal();

		if (conn_)
			conn_->revoke();
	}

	// ...

protected:
	// DEBUG {
	bool get_exit_flag()
	{
		pw::lockmutex lm(exit_mutex_);
		return exit_flag_;
	}

	bool exit_flag_;
	bool exit_confirm_flag_;
	pw::pmutex exit_mutex_;
	pw::event exit_event_;
	pw::event exit_confirm_event_;
	// DEBUG }
	// ...
	// address of server to connect to
	sockaddr_in server_; 
	// the encapsulation of a socket connection
	connection *conn_; 
};
// END

